package agent

import (
	"net/url"
	"os"
	"os/signal"
	"time"

	"github.com/gofiber/fiber/v2"
	"github.com/gorilla/websocket"
	logger "github.com/sirupsen/logrus"
	"github.com/tidwall/gjson"

	"publish-manage/internal/model"
	"publish-manage/internal/service"
	"publish-manage/internal/ws"
	"publish-manage/pkg/config"
	"publish-manage/pkg/server"
)

func InitRouter() {
	server.Post("/api/v1/publish", Protected(), PublishController.Publish)

}

func Protected() fiber.Handler {
	return func(ctx *fiber.Ctx) error {
		if ctx.Get("Authorization") == "123456abcdefg" {
			return ctx.Next()
		}
		return ctx.SendStatus(fiber.StatusForbidden)
	}
}

//InitWebsocket 连接管理端websocket，该方法阻塞
func InitWebsocket() {
	interrupt := make(chan os.Signal, 1)
	signal.Notify(interrupt, os.Interrupt)

	u := url.URL{Scheme: "ws", Host: config.GetString("server.manageAddr"), Path: "/ws/" + config.GetString("server.agentId")}
	logger.Debug("链接到", u.String())

	c, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
	if err != nil {
		logger.Fatal(err)
	}
	defer c.Close()

	done := make(chan struct{})

	go func() {
		defer close(done)
		for {
			_, message, goErr := c.ReadMessage()
			if goErr != nil {
				logger.Error(goErr)
				return
			}
			dealMessage(message, c)
		}
	}()

	serverInfoTicker := time.NewTicker(time.Minute)
	defer serverInfoTicker.Stop()

	pingTicker := time.NewTicker(60 * time.Second)
	defer pingTicker.Stop()

	for {
		select {
		case <-done:
			return
		case t := <-pingTicker.C:
			err = c.WriteMessage(websocket.PingMessage, []byte(t.String()))
			if err != nil {
				logger.Error("Ping Error:", err)
				return
			}
		case <-serverInfoTicker.C:
			if err = c.WriteJSON(&ws.SendInfo{
				Type: ws.TypeServerInfo,
				Data: &ws.ServerInfo{
					Cpu:    service.ServerInfoService.GetCpuPercent(time.Minute),
					Memory: service.ServerInfoService.GetMemoryInfo(),
					Disk:   service.ServerInfoService.GetDiskUsage(),
				},
			}); err != nil {
				logger.Error(err)
			}
		case <-interrupt:
			logger.Warn("interrupt signal received")
			err = c.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
			if err != nil {
				logger.Error(err)
				return
			}
			select {
			case <-done:
			case <-time.After(3 * time.Second):
			}
			return
		}
	}
}

func dealMessage(message []byte, c *websocket.Conn) {
	js := gjson.ParseBytes(message)
	t := js.Get("type").Int()
	switch t {
	case ws.TypeAgentId:
		tryUpdateAgentId(js.Get("data").String())
	case ws.TypePublishRequest: // 部署请求
		if err := service.AgentPublishService.PublishPackage(&model.AgentPackage{
			Id:        js.Get("data.id").String(),
			AgentId:   js.Get("data.agentId").String(),
			PackageId: js.Get("data.packageId").String(),
			Type:      int(js.Get("data.type").Int()),
			Path:      js.Get("data.path").String(),
			ZipDir:    js.Get("data.zipDir").String(),
			Command:   js.Get("data.command").String(),
		}); err != nil {
			c.WriteJSON(&ws.SendInfo{
				Type: ws.TypePublishResult,
				Data: false,
				Msg:  err.Error(),
			})
		} else {
			c.WriteJSON(&ws.SendInfo{
				Type: ws.TypePublishResult,
				Data: true,
			})
		}
	}
}

func tryUpdateAgentId(agentId string) {
	myId := config.GetString("server.agentId")
	if agentId != myId {
		config.Set("server.agentId", agentId)
		config.Save()
	}
}
